package models

import (
	"fmt"
	"time"

	"gorm.io/gorm"
)

const (
	// NvdType :
	NvdType = "NVD"
	// JvnType :
	JvnType = "JVN"

	// NvdExactVersionMatch :
	NvdExactVersionMatch = "NvdExactVersionMatch"
	// NvdRoughVersionMatch :
	NvdRoughVersionMatch = "NvdRoughVersionMatch"
	// NvdVendorProductMatch :
	NvdVendorProductMatch = "NvdVendorProductMatch"
	// JvnVendorProductMatch :
	JvnVendorProductMatch = "JvnVendorProductMatch"
)

// GetURLByYear returns url
func GetURLByYear(source, year string) (url string, err error) {
	switch source {
	case NvdType:
		return fmt.Sprintf("https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-%s.json.gz", year), nil
	case JvnType:
		switch year {
		case "modified":
			return "https://jvndb.jvn.jp/ja/rss/jvndb.rdf", nil
		case "recent":
			return "https://jvndb.jvn.jp/ja/rss/jvndb_new.rdf", nil
		default:
			return fmt.Sprintf("https://jvndb.jvn.jp/ja/rss/years/jvndb_%s.rdf", year), nil
		}
	default:
		return "", fmt.Errorf("unknown source type: %s", source)
	}
}

// LatestSchemaVersion manages the Schema version used in the latest go-cve-dictionary.
const LatestSchemaVersion = 2

// FetchMeta has meta information about fetched CVE data
type FetchMeta struct {
	gorm.Model        `json:"-"`
	GoCVEDictRevision string
	SchemaVersion     uint
	LastFetchedAt     time.Time
}

// OutDated checks whether last fetched feed is out dated
func (f FetchMeta) OutDated() bool {
	return f.SchemaVersion != LatestSchemaVersion
}

// CveDetail :
type CveDetail struct {
	CveID string
	Nvds  []Nvd
	Jvns  []Jvn
}

// HasNvd returns true if NVD contents
func (c CveDetail) HasNvd() bool {
	return len(c.Nvds) != 0
}

// HasJvn returns true if JVN contents
func (c CveDetail) HasJvn() bool {
	return len(c.Jvns) != 0
}

// CpeDetail :
type CpeDetail struct {
	Nvds []NvdCpe
	Jvns []JvnCpe
}

// Components common to Nvd and Jvn

// Cvss2 has CVSS Version 2 info
type Cvss2 struct {
	VectorString          string `gorm:"type:varchar(255)"`
	AccessVector          string `gorm:"type:varchar(255)"`
	AccessComplexity      string `gorm:"type:varchar(255)"`
	Authentication        string `gorm:"type:varchar(255)"`
	ConfidentialityImpact string `gorm:"type:varchar(255)"`
	IntegrityImpact       string `gorm:"type:varchar(255)"`
	AvailabilityImpact    string `gorm:"type:varchar(255)"`
	BaseScore             float64
	Severity              string `gorm:"type:varchar(255)"`
}

// Cvss3 has CVSS Version 3 info
type Cvss3 struct {
	VectorString          string `gorm:"type:varchar(255)"`
	AttackVector          string `gorm:"type:varchar(255)"`
	AttackComplexity      string `gorm:"type:varchar(255)"`
	PrivilegesRequired    string `gorm:"type:varchar(255)"`
	UserInteraction       string `gorm:"type:varchar(255)"`
	Scope                 string `gorm:"type:varchar(255)"`
	ConfidentialityImpact string `gorm:"type:varchar(255)"`
	IntegrityImpact       string `gorm:"type:varchar(255)"`
	AvailabilityImpact    string `gorm:"type:varchar(255)"`
	BaseScore             float64
	BaseSeverity          string `gorm:"type:varchar(255)"`
	ExploitabilityScore   float64
	ImpactScore           float64
}

// CpeBase has common args of Cpe and EnvCpe
type CpeBase struct {
	URI                   string `gorm:"index;type:varchar(255)"`
	FormattedString       string `gorm:"index;type:varchar(255)"`
	WellFormedName        string `gorm:"type:text"`
	CpeWFN                `gorm:"embedded"`
	VersionStartExcluding string `gorm:"type:varchar(255)"`
	VersionStartIncluding string `gorm:"type:varchar(255)"`
	VersionEndExcluding   string `gorm:"type:varchar(255)"`
	VersionEndIncluding   string `gorm:"type:varchar(255)"`
}

// CpeWFN has CPE Well Formed name information
type CpeWFN struct {
	Part            string `gorm:"index;type:varchar(255)"`
	Vendor          string `gorm:"index;type:varchar(255)"`
	Product         string `gorm:"index;type:varchar(255)"`
	Version         string `gorm:"type:varchar(255)"`
	Update          string `gorm:"type:varchar(255)"`
	Edition         string `gorm:"type:varchar(255)"`
	Language        string `gorm:"type:varchar(255)"`
	SoftwareEdition string `gorm:"type:varchar(255)"`
	TargetSW        string `gorm:"type:varchar(255)"`
	TargetHW        string `gorm:"type:varchar(255)"`
	Other           string `gorm:"type:varchar(255)"`
}

// Reference holds reference information about the CVE.
type Reference struct {
	Link   string `gorm:"type:text"`
	Source string `gorm:"type:varchar(255)"`
	Tags   string `gorm:"type:varchar(255)"`
	Name   string `gorm:"type:text"`
}

// Cert holds CERT alerts.
type Cert struct {
	Title string `gorm:"type:text"`
	Link  string `gorm:"type:text"`
}

// Nvd is a struct of NVD JSON
// https://scap.nist.gov/schema/nvd/feed/0.1/nvd_cve_feed_json_0.1_beta.schema
type Nvd struct {
	ID               int64  `json:"-"`
	CveID            string `gorm:"index:idx_nvds_cveid;type:varchar(255)"`
	Descriptions     []NvdDescription
	Cvss2            NvdCvss2Extra
	Cvss3            NvdCvss3
	Cwes             []NvdCwe
	Cpes             []NvdCpe
	References       []NvdReference
	Certs            []NvdCert
	PublishedDate    time.Time
	LastModifiedDate time.Time

	DetectionMethod string `gorm:"-"`
}

// NvdDescription has description of the CVE
type NvdDescription struct {
	ID    int64  `json:"-"`
	NvdID uint   `json:"-" gorm:"index:idx_nvd_descriptions_nvd_id"`
	Lang  string `gorm:"type:varchar(255)"`
	Value string `gorm:"type:text"`
}

// NvdCvss2Extra has Nvd extra CVSS V2 info
type NvdCvss2Extra struct {
	ID                      int64 `json:"-"`
	NvdID                   uint  `json:"-" gorm:"index:idx_nvd_cvss2_extra_nvd_id"`
	Cvss2                   `gorm:"embedded"`
	ExploitabilityScore     float64
	ImpactScore             float64
	ObtainAllPrivilege      bool
	ObtainUserPrivilege     bool
	ObtainOtherPrivilege    bool
	UserInteractionRequired bool
}

// NvdCvss3 has Nvd CVSS3 info
type NvdCvss3 struct {
	ID    int64 `json:"-"`
	NvdID uint  `json:"-" gorm:"index:idx_nvd_cvss3_nvd_id"`
	Cvss3 `gorm:"embedded"`
}

// NvdCwe has CweID
type NvdCwe struct {
	ID    int64  `json:"-"`
	NvdID uint   `json:"-" index:"idx_nvd_cwes_nvd_id"`
	CweID string `gorm:"type:varchar(255)"`
}

// NvdCpe is Child model of Nvd.
// see https://www.ipa.go.jp/security/vuln/CPE.html
// In NVD,
// configurations>nodes>cpe>vulnerable: true
type NvdCpe struct {
	ID      int64 `json:"-"`
	NvdID   uint  `json:"-" gorm:"index:idx_nvd_cpes_nvd_id"`
	CpeBase `gorm:"embedded"`
	EnvCpes []NvdEnvCpe
}

// NvdEnvCpe is a Environmental CPE
// Only NVD has this information.
// configurations>nodes>cpe>vulnerable: false
type NvdEnvCpe struct {
	ID       int64 `json:"-"`
	NvdCpeID uint  `json:"-" gorm:"index:idx_nvd_env_cpes_nvd_cpe_id"`
	CpeBase  `gorm:"embedded"`
}

// NvdReference holds reference information about the CVE.
type NvdReference struct {
	ID        int64 `json:"-"`
	NvdID     uint  `json:"-" gorm:"index:idx_nvd_references_nvd_id"`
	Reference `gorm:"embedded"`
}

// NvdCert is Child model of Nvd.
type NvdCert struct {
	ID    int64 `json:"-"`
	NvdID uint  `json:"-" gorm:"index:idx_nvd_certs_nvd_id"`
	Cert  `gorm:"embedded"`
}

// Jvn is a model of JVN
type Jvn struct {
	ID               int64  `json:"-"`
	CveID            string `gorm:"index:idx_jvns_cveid;type:varchar(255)"`
	Title            string `gorm:"type:varchar(255)"`
	Summary          string `gorm:"type:text"`
	JvnLink          string `gorm:"type:varchar(255)"`
	JvnID            string `gorm:"type:varchar(255)"`
	Cvss2            JvnCvss2
	Cvss3            JvnCvss3
	Cpes             []JvnCpe
	References       []JvnReference
	Certs            []JvnCert
	PublishedDate    time.Time
	LastModifiedDate time.Time

	DetectionMethod string `gorm:"-"`
}

// JvnCvss2 has Jvn CVSS Version 2 info
type JvnCvss2 struct {
	ID    int64 `json:"-"`
	JvnID uint  `json:"-" gorm:"index:idx_jvn_cvss2_jvn_id"`
	Cvss2 `gorm:"embedded"`
}

// JvnCvss3 has JVN CVSS3 info
type JvnCvss3 struct {
	ID    int64 `json:"-"`
	JVNID uint  `json:"-" gorm:"index:idx_jvn_cvss3_jvn_id"`
	Cvss3 `gorm:"embedded"`
}

// JvnCpe is Child model of Jvn.
// see https://www.ipa.go.jp/security/vuln/CPE.html
type JvnCpe struct {
	ID      int64 `json:"-"`
	JvnID   uint  `json:"-" gorm:"index:idx_jvn_cpes_jvn_id"`
	CpeBase `gorm:"embedded"`
}

// JvnReference is Child model of Jvn.
type JvnReference struct {
	ID        int64 `json:"-"`
	JvnID     uint  `json:"-" gorm:"index:idx_jvn_references_jvn_id"`
	Reference `gorm:"embedded"`
}

// JvnCert is Child model of Jvn.
type JvnCert struct {
	ID    int64 `json:"-"`
	JvnID uint  `json:"-" gorm:"index:idx_jvn_certs_jvn_id"`
	Cert  `gorm:"embedded"`
}
